// 将下面异步代码使用 Promise 的方式改进
setTimeout(function () {
    var a = "hello";
    setTimeout(function () {
        var b = "lagou";
        setTimeout(function () {
            var c = "I ♥ U";
            console.log(a + b + c);
        }, 10);
    }, 10);
}, 10);

function p(value) {
    return new Promise((resolve, reject) => [
        setTimeout(() => {
            resolve(value);
        }, 10),
    ]);
}

p("hello")
    .then((res) => {
        return p(res + "lagou");
    })
    .then((res) => {
        return p(res + "I ♥ U");
    })
    .then((res) => {
        console.log(res);
    });

// --------------------------------------------------------------------------------

const fp = require("lodash/fp");
// horsepower 马力, dollar_value 价格, in_stock 库存
const cars = [
    {
        name: "Ferrair FF",
        horsepower: 660,
        dollar_value: 700000,
        in_stock: true,
    },
    {
        name: "Spyker C12 Zagato",
        horsepower: 650,
        dollar_value: 648000,
        in_stock: false,
    },
    {
        name: "Jafuar XKR-S",
        horsepower: 550,
        dollar_value: 132000,
        in_stock: false,
    },
    { name: "Audi R8", horsepower: 525, dollar_value: 114200, in_stock: false },
    {
        name: "Aston Martin One-77",
        horsepower: 750,
        dollar_value: 1850000,
        in_stock: true,
    },
    {
        name: "Pagani Huayra",
        horsepower: 700,
        dollar_value: 1300000,
        in_stock: false,
    },
];

// 1.使用函数组合fp.flowRight() 重新实现下面这个函数
let isLastInStock = function (cars) {
    // 获取最后一条数据
    let last_car = fp.last(cars);
    // 获取最后一条数据的 in_stock属性值
    return fp.prop("in_stock", last_car);
};
console.log("lastInStock", isLastInStock(cars));
// 实现:
let lastInStock = fp.flowRight(fp.prop("in_stock"), fp.last);
console.log("lastInStock", lastInStock(cars));

// 2.使用fp.flowRight(),fp.prop(),和fp.first()获取第一个car的name
// 实现:
let firstCarName = fp.flowRight(fp.prop("name"), fp.first);
console.log("firstCarName", firstCarName(cars));

// 3.使用帮助函数_average重构averageDollarValue,使用函数组合的方式实现
let _average = function (xs) {
    return fp.reduce(fp.add, 0, xs) / xs.length;
};
let averageDollarValue = function (cars) {
    let dollar_values = fp.map(function (car) {
        return car.dollar_value;
    }, cars);
    return _average(dollar_values);
};
console.log("averageDollarValue", averageDollarValue(cars));

// 实现:

let averageDollarValue2 = fp.flowRight(
    _average,
    fp.map((x) => x.dollar_value)
);
console.log("averageDollarValue", averageDollarValue2(cars));

// 4.使用flowRight 写一个sanitizeNames()函数,返回一个下划线连接小写的字符串,把数组中的name转换成为这种形式: 例如: sanitizeNames(["Hello World"]) => ['hello_world"]
let _underscore = fp.replace(/\W+/g, "_");

let sanitizeNames = fp.flowRight(fp.map((x) => _underscore(x)));
console.log(sanitizeNames(["Hello World"]));

// ----------------------------------------------------------------------------------------

const { Maybe, Container } = require("./support");
// 1.使用fp.add(x,y)和fp.map(f,x)创建一个能让functor里的值增加的函数 ex1
let maybe = Maybe.of([5, 6, 1]);
let ex1 = (n) => {
    let fn = fp.flowRight(fp.map(fp.add(n)));
    return maybe.map(fn);
};
console.log(ex1(1));

// 2.实现一个函数ex2, 能够使用fp.first获取列表的第一个元素
let xs = Container.of(["do", "ray", "me", "fa", "so", "la", "ti", "do"]);
let ex2 = () => {
    return xs.map(fp.first)._value;
};
console.log(ex2());

// 3.实现一个函数ex3, 使用safeProp和fp.first找到 user的名字的首字母
let safeProp = fp.curry(function (x, o) {
    return Maybe.of(o[x]);
});
let user = { id: 2, name: "Albert" };
let ex3 = () => {
    return safeProp("name", user).map(fp.first)._value;
};
console.log(ex3());

// 4. 使用Maybe重写ex4, 不要有if语句
let ex4 = function (n) {
    if (n) {
        return parseInt(n);
    }
};
let _ex4 = function (n) {
    return Maybe.of(n).map(parseInt)._value;
};
console.log(ex4("5"));
console.log(_ex4("5"));

// 四、手写实现MyPromise源码

/** 
1.Promise是一个类,执行这个类时需要传递一个执行器,执行器是立即执行的
2.Promise中有三种状态,成功,失败,等待,状态一旦确定不可更改
3.resolve和reject函数是用来更改状态的
4.then方法内部做的事情就是判断状态,如果状态是成功,调用成功回调,如果是失败,调用失败的回调函数,then方法是被定义在原型对象中的方法
5.then成功回调有一个参数 表示成功之后的值,then失败回调有一个参数,表示失败后的原因
6.同一个promise对象下面的then方法是可以被多次调用的
7.then方法是可以被链式调用的,后面的then方法回调函数拿到的值是上一个then方法回调函数的返回值
*/

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
    // 执行器
    constructor(executor) {
        try {
            executor(this.resolve, this.reject);
        } catch (error) {
            // 执行器内部错误直接调用错误处理函数
            this.reject(error);
        }
    }
    // 默认状态是等待
    status = PENDING;
    // 成功之后的值
    value = undefined;
    // 失败之后的值
    reason = undefined;
    // 成功之后的回调
    successCallback = [];
    // 失败之后的回调
    failCallback = [];
    // 静态all方法 接收的是一个需要并行执行的函数
    static all(array) {
        let result = [];
        let index = 0;
        // all方法返回的也是promise实例
        return new MyPromise((resolve, reject) => {
            // 定义一个加入数据的函数
            function addData(key, value) {
                result[key] = value;
                index++;
                // 所有的操作完成,主要是异步操作完成之后才将所有的结果resolve出去
                if (index === array.length) {
                    resolve(result);
                }
            }
            // 遍历array数组内的值
            for (let i = 0; i < array.length; i++) {
                // 保存当前值
                let current = array[i];
                // 判断当前值是普通值还是promise对象的实例
                if (current instanceof MyPromise) {
                    current.then(
                        (value) => addData(i, value),
                        (reason) => reject(reason)
                    );
                } else {
                    addData(i, array[i]);
                }
            }
        });
    }
    // 静态resolve 方法 将传入的参数变为promise对象
    static resolve(value) {
        // 如果已经是promise对象了,那么就返回传入的值
        if (value instanceof MyPromise) return value;
        // 如果是普通值则new一个 promise内部再resolve出去值
        return new Promise((resolve) => resolve(value));
    }
    // 定义成箭头函数目的是为了让this指向类的实例
    resolve = (value) => {
        // 如果不是等待状态则直接返回
        if (this.status !== PENDING) return;
        // 将状态改为成功
        this.status = FULFILLED;
        // 将值保存
        this.value = value;
        // 如果存在成功回调，则调用,因为存储的回调是一个数组,所以需要依次执行
        while (this.successCallback.length) {
            this.successCallback.shift()();
        }
    };
    reject = (reason) => {
        // 一样的，如果不是等待状态就返回
        if (this.status !== PENDING) return;
        // 将状态改为失败
        this.status = FULFILLED;
        // 保存失败原因
        this.reason = reason;
        // 每执行一次失败/成功回调则弹出一个
        while (this.failCallback.length) {
            this.failCallback.shift()();
        }
    };
    // 定义原型对象上的then函数
    then(successCallback, failCallback) {
        // 如果有传入成功或者失败的回调则使用,否则是一个继续向下传递值的函数
        successCallback = successCallback ? successCallback : (value) => value;
        failCallback = failCallback
            ? failCallback
            : (reason) => {
                  throw reason;
              };
        // 要实现then方法的链式调用,必须要让then方法返回一个promise对象
        let promise2 = new MyPromise((resolve, reject) => {
            // 判断状态如果是成功则调用成功的回调，失败则调用失败的回调
            if (this.status === FULFILLED) {
                // 内部无法直接拿到promise2这个实例,所以这里的setTimeout的作用就是为了拿到promise2实例,让执行变为异步
                setTimeout(() => {
                    try {
                        // 判断回调返回值是普通值还是promise对象,
                        // 如果是普通值则直接调用resolve,如果是promise对象,查看promise对象的返回结果,再根据返回的结果决定调用resolve还是reject
                        let x = successCallback(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            } else if (this.status === FULFILLED) {
                // setTimeout只是为了拿到promise2
                setTimeout(() => {
                    try {
                        let x = failCallback(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            } else {
                // 可以让then方法多次调用,异步状态存储then
                this.successCallback.push(() => {
                    // setTimeout只是为了拿到promise2
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
                this.failCallback.push(() => {
                    // setTimeout只是为了拿到promise2
                    setTimeout(() => {
                        try {
                            let x = failCallback(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
            }
        });
        // 将promise实例再返回,因为then返回的是一个promise实例
        return promise2;
    }
    // finally内部无论是成功还是失败都会执行,而且返回当前promise对象最终返回的结果
    finally(callback) {
        // 谁调用finally最终返回的结果就是谁的,传入的参数也是调用者定义的值
        return this.then(
            (value) => {
                return MyPromise.resolve(callback()).then(() => value);
            },
            (reason) => {
                return MyPromise.resolve(callback()).then(() => {
                    throw reason;
                });
            }
        );
    }
    // catch方法处理当前promise对象最终状态为失败的情况
    catch(failCallback) {
        // catch方法的内部也是调用的then方法,成功回调为undefined
        return this.then(undefined, failCallback);
    }
}
// 定义判断回调的类型
function resolvePromise(promise2, x, resolve, reject) {
    // 如果then方法返回的promise和成功回调返回的promise是同一个对象则程序报类型错误
    if (promise2 === x)
        return reject(
            new TypeError("Chaining cycle detected for promise #<Promise>")
        );

    // 判断如果是MyPromise的实例才调用then方法,根据传入的参数来判断是执行resolve还是reject
    if (x instanceof MyPromise) {
        x.then(resolve, reject);
    } else {
        // 如果是普通值则直接resolve;
        resolve(x);
    }
}

new MyPromise((resolve, reject) => {
    resolve("123");
})
    .finally(() => {
        console.log("456");
        return "789";
    })
    .then((res) => {
        console.log(res);
    });
